﻿using Manina.Windows.Forms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LicenseAutoDetection
{
    /*
     * 이미지 검색을 별도의 쓰레드로 구현
     * 이미 쓰레드가 실행중인 경우 기존 동작을 멈추기 위해서 Thread.Abort()와 같이 사용하는 것은 문제가 많음
     * 대신에 Task와 CancellationTokenSource를 사용해서 Cancel 동작을 처리
     * 
     * 참고: http://msdn.microsoft.com/en-us/library/dd537607%28v=vs.110%29.aspx
     */
    class SearchTask : IDisposable
    {
        Task task;
        CancellationTokenSource tokenSource;
        Settings settings;
        MainForm mainForm;
        ProgressReporter ui = new ProgressReporter();
        AnalyzeTask analyzeTask;
        string[] ext;

        public Boolean IsCompleted
        {
            get
            {
                return task == null || task.IsCompleted;
            }
        }

        public SearchTask(Settings sets, MainForm form)
        {
            settings = sets;
            mainForm = form;
        }

        public void Start(string path)
        {
            Start(path, true);
        }

        public void Start(string path, Boolean all)
        {
            Cancel(); // 기존 쓰레드가 동작중 일 경우 cancel 시킴
            tokenSource = new CancellationTokenSource();
            analyzeTask = new AnalyzeTask(mainForm);
            ext = settings.Extension;

            if (all)
            {
                // 하위 디렉토리도 같이 검색
                task = Task.Factory.StartNew(() => SearchAll(path, tokenSource.Token), tokenSource.Token);
            }
            else
            {
                // 현재 디렉토리만 검색
                task = Task.Factory.StartNew(delegate() { Search(path, tokenSource.Token); }, tokenSource.Token);
            }
        }

        public void Cancel()
        {
            if (task != null && !task.IsCompleted)
            {
                tokenSource.Cancel();
            }
        }

        /*
         * 선택된 디렉토리만 검색
         */
        private void Search(string path, CancellationToken ct)
        {
            ui.Update(() => {
                mainForm.UpdateMenuStateByStart(true);
                mainForm.ClearPathList(true);
            });

            SearchImages(path, ct);

            analyzeTask.Start(ct);
        }

        private void SearchImages(string path, CancellationToken ct)
        {
            ui.Update(() => mainForm.UpdateStatusStrip(path, "검색중", -1));
            List<string> files = new List<string>();

            foreach (string e in ext)
            {
                if (ct.IsCancellationRequested)
                {
                    return;
                }

                try
                {
                    files.AddRange(Directory.GetFiles(path, "*." + e));
                }
                catch
                {
                    continue;
                }
            }

            ui.Update(() =>
            {
                mainForm.AllSearched.AddRange(files.ToArray());
            });
        }

        /*
         * 선택된 디렉토리 이하 모든 디렉토리를 검색
         */
        private void SearchAll(string path, CancellationToken ct)
        {
            ui.Update(() => {
                mainForm.UpdateMenuStateByStart(true);
                mainForm.ClearPathList(true);
            });

            if (path.Equals(MainForm.RootDir))
            {
                foreach (DriveInfo di in DriveInfo.GetDrives())
                {
                    if (di.IsReady)
                    {
                        SearchAllImages(di.Name, ct);
                    }
                }
            }
            else
            {
                SearchAllImages(path, ct);
            }

            analyzeTask.Start(ct);
        }

        private void SearchAllImages(string path, CancellationToken ct)
        {
            SearchImages(path, ct);
            string[] dirs;

            try
            {
                dirs = Directory.GetDirectories(path);
            }
            catch
            {
                return;
            }

            foreach (string dir in dirs)
            {
                if (ct.IsCancellationRequested)
                {
                    return;
                }

                if (settings.ExcludeFolder(dir))
                {
                    continue;
                }

                FileInfo fi;

                try
                {
                    fi = new FileInfo(dir);
                }
                catch
                {
                    continue;
                }

                if ((fi.Attributes & FileAttributes.System) != FileAttributes.System &&
                    (settings.IncludeHidden || (fi.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden))
                {
                    SearchAllImages(dir, ct);
                }
            }
        }
        
        #region IDisposable 구현
        // Build - Run code analysis 시 tokenSource가 Dispose()가 필요하다고 나옴
        // 아래 사이트 참고하여 IDisposable 구현
        // - http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface
        // - http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

        private bool disposed = false;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (tokenSource != null) tokenSource.Dispose();
                    if (analyzeTask != null) analyzeTask.Dispose();
                }

                disposed = true;
            }
        }

        ~SearchTask()
        {
            Dispose(false);
        }
        #endregion
    }
}
